import base64, pickle
from django.shortcuts import render
from django_redis import get_redis_connection
from rest_framework.response import Response
from rest_framework import status
from rest_framework.views import APIView

from goods.models import SKU
from .serializers import CartSerializer, CartSKUSerializer, CartDelSerializer,CartSelectAllSerializer


# Create your views here.

class CartView(APIView):
    """购物车增删改查"""

    def perform_authentication(self, request):
        """重写此方法可以延后/禁用认证"""
        # 当第一次 request.user / request.auth就会再去执行认证逻辑
        pass

    def post(self, request):
        """购物车新增"""
        # 创建序列化器进行反序列化
        serializer = CartSerializer(data=request.data)
        # 调用序列化器的检验方法
        serializer.is_valid(raise_exception=True)
        # 获取反序列化校验后的数据   ,使用键查询出她们的值
        sku_id = serializer.validated_data.get('sku_id')
        count = serializer.validated_data.get('count')
        selected = serializer.validated_data.get('selected')

        try:
            user = request.user
            # 当执行这行代码时，还会做认证，但是未登录的用户认证会抛出401异常，
            # 自己捕获异常不让它提前响应
        except:
            user = None

        if user:
            # 登录用户操作redis购物车数据
            # 创建redis连接对象
            redis_conn = get_redis_connection('cart')
            # 创建管道
            pl = redis_conn.pipeline()
            # hincrby() 增量的同时做添加
            # pl.hincrby(name, 3, 1)
            # 如果要添加到key在hash字典中已存在，就会把key对应的value与新添加的value做累加
            # 然后存键累加后的值
            # 如果要添加到key在hash字典中不存在，那么就是一个新增行为
            pl.hincrby('cart_%s' % user.id, sku_id, count)
            # 判断当前商品有没有勾选，如果是勾选再添加到set集合
            if selected:
                # 把勾选商品的sku_id 添加到set集合
                pl.sadd('selected_%s' % user.id, sku_id)

            # 执行管道
            pl.execute()

            # 响应
            return Response(serializer.data, status=status.HTTP_201_CREATED)

        else:
            # 未登录用户操作cookie购物车数据
            # 先获取出cookie购物车数据 , 取出来的数据为str，字符串类型
            cart_str = request.COOKIES.get('cart')
            print(type(cart_str))
            # 判断购物车中cookie中是否有数据
            if cart_str:
                # 先把str的购物车数据转换成bytes
                cart_str_bytes = cart_str.encode()
                # 再调用baseb4.loads() 把bytes类型的字符串转成bytes类型的ASCII
                cart_str_ascii = base64.b64decode(cart_str_bytes)
                # 再调用pickle.loads()把bytes类型的数据ASCII转换成字典
                cart_dict = pickle.loads(cart_str_ascii)
                print(cart_dict)  #

            else:
                cart_dict = {}
            # 判断当前要添加的sku_id在cookie字典中是否已存在，如果已存在，取出原sku_id的count和本次添加的count累加
            if sku_id in cart_dict:
                print(type(sku_id))

                origin_count = cart_dict[sku_id]['count']
                count += origin_count

            # 添加到cookie购物车字典中
            cart_dict[sku_id] = {
                'count': count,
                'selected': selected
            }
            print(cart_dict)
            # 把cookie字典转换成 bytes类型的ASCII  pickle.dumps()
            cart_str_ascii = pickle.dumps(cart_dict)
            # 调用base64.b64encode()把bytes类型的ASCII转换成bytes类型的str
            cart_str_bytes = base64.b64encode(cart_str_ascii)
            # 调用decode()把bytes类型str转成str
            cart_str = cart_str_bytes.decode()
            print(cart_str)
            # 设置到cookie
            response = Response(serializer.data, status=status.HTTP_201_CREATED)
            response.set_cookie('cart', cart_str)
            # 响应

            return response

    def get(self, request):
        """购物车查询"""

        try:
            user = request.user  # 查询用户
        except:
            user = None

        if user:
            # 登录用户获取redis购物车数据
            # 创建redis连接对象
            redis_conn = get_redis_connection('cart')
            # 取出hash字典中的数据{sku_id: count}
            cart_redis = redis_conn.hgetall('cart_%s' % user.id)
            # 取出set集合中的数据{sku_id}
            selected_ids = redis_conn.smembers('selected_%s' % user.id)
            # 定义一个字典，进行把redis购物车数据转换成和cookie购物车数据格式一样
            cart_dict = {}  # 将来和cookie字典模型及变量名都一样
            for sku_id_bytes, count_bytes in cart_redis.items():
                cart_dict[int(sku_id_bytes)] = {
                    'count': int(count_bytes),
                    'selected': sku_id_bytes in selected_ids
                }
            print(cart_dict)

        else:
            # 未登录用户获取cookie购物车数据
            # 获取cookie数据
            cart_str = request.COOKIES.get('cart')
            if cart_str:
                # 把cookie字符串转换回字典
                cart_bytes = cart_str.encode()
                cart_ascii = base64.b64decode(cart_bytes)
                cart_dict = pickle.loads(cart_ascii)
                # cart_dict = pickle.loads(base64.b64decode(cart_str.encode()))
                print(cart_dict)
                print(type(cart_dict))

            else:
                return Response(status=status.HTTP_400_BAD_REQUEST)

        # 获取sku_id 对应的模型并且做相对应的序列化
        sku_ids = cart_dict.keys()
        print(sku_ids)
        skus = SKU.objects.filter(id__in=sku_ids)
        for sku in skus:
            sku.count = cart_dict[sku.id]['count']
            sku.selected = cart_dict[sku.id]['selected']

        # 响应
        serializer = CartSKUSerializer(skus, many=True)

        return Response(serializer.data)

    def put(self, request):
        """购物车修改"""
        serializer = CartSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        sku_id = serializer.validated_data.get('sku_id')
        count = serializer.validated_data.get('count')
        selected = serializer.validated_data.get('selected')

        try:
            user = request.user
        except:
            user = None

        response = Response(serializer.data)

        if user:
            # 登录用户修改redis购物车数据
            # 创建redis连接对象
            redis_conn = get_redis_connection('cart')
            # 创建管道
            pl = redis_conn.pipeline()
            # 直接设置hash字典 如果sku_id在hash字典中已存在，直接新值覆盖旧值
            pl.hset('cart_%s' % user.id, sku_id, count)

            # 判断selected是勾选还是未勾选
            if selected:
                # 如果勾选把当前sku_id 添加到set集合中
                pl.sadd('selected_%s' % user.id, sku_id)
            else:
                # 如果未勾选把当前sku_id从set集合中移除
                pl.srem('selected_%s' % user.id, sku_id)

            # 执行管道
            pl.execute()
        # 响应
        # return Response(serializer.data)
        else:
            #    未登录用户修改cookie购物车数据
            # 获取cookie购物车数据
            cart_str = request.COOKIES.get('cart') #'gAN9cQBLEH1xAShYBQAAAGNvdW50cQJLBFgIAAAAc2VsZWN0ZWRxA4h1cy4='
            if cart_str:
                # 把字符串类型的购物车数据转换成字典
                cart_dict = pickle.loads(base64.b64decode(cart_str.encode()))
            else:
                return Response(status=status.HTTP_400_BAD_REQUEST)
            # 对cart_dict 直接重新赋值
            cart_dict[sku_id] = {
                'count': count,
                'selected': selected
            }
            # 把购物车字典转成ModelSerializer字符串
            cart_str = base64.b64encode(pickle.dumps(cart_dict)).decode()
            # 设置cookie
            response.set_cookie('cart', cart_str)
            # 响应

        return response




    def delete(self, request):
        """购物车删除"""
        serializer = CartDelSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        sku_id = serializer.validated_data.get('sku_id')

        try:
           user = request.user
        except:
            user = None

        if user:
            """登录用户操作redis购物车数据"""
            #创建数据库连接
            redis_conn = get_redis_connection('cart')
            #创建管道
            pl = redis_conn.pipeline()
            #把当前要删除的sku_id 从hash字典中移除
            pl.hdel('cart_%s' % user.id , sku_id)
            #把当前要删除的sku_id 从set集合中移除
            pl.srem('selected_%s' % user.id , sku_id)
            #执行管道
            pl.execute()
            #响应

            return Response(status=status.HTTP_204_NO_CONTENT)

        else:
            """未登录的用户操作cookie数据"""
            #获取请求对象的cookie购物车数据
            cart_str = request.COOKIES.get('cart')
            if cart_str:  #判断cookie是否取出来
            #把str转换成dict
                cart_dict = pickle.loads(base64.b64decode(cart_str.encode()))
            else:
                return Response(status=status.HTTP_400_BAD_REQUEST)
            # 判断当前要删除的sku_id在字典中是否存在
            if sku_id in cart_dict:
                # del_dict[sku_id]
                print('1111_%s' % sku_id)
                del cart_dict[sku_id]
                print('2222_%s' % sku_id)
            #把dict转换成str
            cart_str = base64.b64encode(pickle.dumps(cart_dict)).decode()

            #设置cookie覆盖原有cookie数据
            response = Response(status=status.HTTP_204_NO_CONTENT)

            response.set_cookie('cart', cart_str)
            # 响应
            return response


class CartSelectAllView(APIView):
    """购物车的全选"""
    def perform_authentication(self, request):
        """重写此方法可以延后/禁用认证"""
        # 当第一次 request.user / request.auth就会再去执行认证逻辑
        pass

    def put(self,request):
        # 创建序列化器进行反序列化校验
        serializer = CartSelectAllSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        selected = serializer.validated_data.get('selected')
        try:
            user = request.user
        except:
            user = None

        response = Response(serializer.data)
        if user:
            """登录用户操作redis数据"""
            #创建redis连接对象
            redis_conn = get_redis_connection('cart')
           #这里不使用管道，管道获取数据的时候，只有在最后面执行管道的时候，才执行管道，这里不建议使用
            # 获取hash中的所有购物车商品sku_id:count{b'1':b'2',b'18':b'3'}
            cart_redis_dict = redis_conn.hgetall('cart_%s' % user.id)
            print(cart_redis_dict)

            #把hash字典中所有的key获取出来
            sku_ids = cart_redis_dict.keys()
            print(sku_ids)

            if selected:
                # 如果勾选把当前sku_id 添加到set集合中
                redis_conn.sadd('selected_%s' % user.id, *sku_ids)
            else:
                # 如果未勾选把当前sku_id从set集合中移除
                redis_conn.srem('selected_%s' % user.id, *sku_ids)

            #响应
            return Response(serializer.data)


        else:
            """未登录用户操作cookie数据"""
            #    未登录用户修改cookie购物车数据
            # 获取cookie购物车数据
            cart_str = request.COOKIES.get('cart')
            if cart_str:
                #把str转换成dict
                cart_dict = pickle.loads(base64.b64decode(cart_str.encode()))
            else:
                return Response(status=status.HTTP_400_BAD_REQUEST)

            #遍历cart_dict字典对内部的小字典中的selected key对应的value进行修改
            for  sku_id in cart_dict:
                # cart_dict[sku_id]['selected'] = selected
                if selected:
                    cart_dict[sku_id]['selected'] = True
                else:
                    cart_dict[sku_id]['selected'] = False

            # 把字典转换成字符串
            cart_str = base64.b64encode(pickle.dumps(cart_dict)).decode()
            # 设置cookie
            response.set_cookie('cart', cart_str)

        return response